
class C {
  #x = initializeX.call(this, 1) // 使用 initializeX 初始化时指定 C 构造函数实例作为 this
  get x() {
    return this.#x
  }
  set x(val) {
    return this.#x = val
  }
}

// c.prototype 获取自有属性 x 的访问器描述符
let { set: oldSet, get: oldGet } = Object.getOwnPropertyDescriptor(C.prototype, 'x')

// accessor 自动访问器 装饰器逻辑
function logged(value, context) {
  console.log(value)
  console.log(context)
  if (context.kind === 'accessor') {
    const { get: oldGet, set: oldSet } = value // 获取原始的 get 和 set
    return { // 返回一个对象 用于重写自动访问器逻辑
      get() { // 装饰器装饰自动访问器属性时 重写 get
        console.log('accessor 自动访问器 get', this)
        // 如果直接调用 oldGet 则 this 指向全局或 undefined
        // 确保 oldGet 在执行时的 this 指向当前对象实例
        return oldGet.call(this)
      },
      set(val) { // 装饰器装饰自动访问器属性时 重写 set
        console.log('accessor 自动访问器 set')
        // 如果直接调用 oldGet 则 this 指向全局或 undefined
        // 确保 oldSet 在执行时的 this 指向当前对象实例
        return oldSet.call(this, val)
      },
      init(initiaValue) { // 装饰器装饰自动访问器属性时 初始化值由 init 函数返回值来决定
        console.log('accesspr 自动访问器装饰器初始化值 init')
        return initiaValue + 1
      }
    }
  }
}


let {
  get: newGet = oldGet,  // 重命名 + 默认值
  set: newSet = oldSet, // 重命名 + 默认值
  init: initializeX = (val) => val // 默认值
} = logged({ get: oldGet, set: oldSet }, { kind: 'accessor', name: 'x' })


// 重写访问器描述符
Object.defineProperty(C.prototype, 'x', { set: newSet, get: newGet })


const c = new C()

console.log(c.x)

c.x = 5

console.log(c.x)


/**
 * 1. 调用 Object.getOwnPropertyDescriptor 取出 C 原型对象上 x 的访问器描述符
 * 2. 调用 logged 函数
 *   2.1 传递原始的 get 和 set
 *   2.2 返回一个对象 拥有新的 get 和 set 和 init 初始化属性函数
 * 3. 调用 Object.defineProperty 给 C 的原型对象设置新的 get 和 set
 * 4.new C 实例化一个实例
 *   4.1 initializeX.call(this, 1) 其中 this 是实例对象 初始值赋值给 #x
 * 5. 调用 c.x 触发新get访问器 ( 内部使用 call 指定 this 为实例 调用原始 oldGet  )
 * 6. 调用 c.x = 5 触发新set访问器 ( 内部使用 call 指定 this 为实例 调用原始 newGet )
 */